/**
    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 2010  Sean Beecroft

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

 * NOTE; This code was found in pastebin under GPL, but I could not find the original author.
 * If you find them let me know!
 * 
 * @version 1.10 2010-01-01
 * @author Sean Beecroft, Unknown
 */

package yawiki.client.gui;

import java.awt.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;


public class YLineNumber extends JComponent implements MouseListener, MouseMotionListener {
    private final static Color DEFAULT_BACKGROUND = new Color(204, 204, 255);
    private final static Color DEFAULT_FOREGROUND = Color.black;
    private final static Font  DEFAULT_FONT = new Font("monospaced", Font.BOLD,
            12);

    // LineNumber height (abends when I use MAX_VALUE)
    private final static int HEIGHT = Integer.MAX_VALUE - 1000000;

    // Set right/left margin
    private final static int MARGIN = 5;

    // Variables for this LineNumber component
    private FontMetrics fontMetrics;
    private int lineHeight;
    private int currentDigits;

    // Metrics of the component used in the constructor
    private JComponent component;
    private int componentFontHeight;
    private int componentFontAscent;

    private Hashtable highlightedLines = new Hashtable();

    public void highlightLine(int line) {	
        try { 
            highlightedLines.put(String.valueOf(line), String.valueOf(line));				
        } catch (Exception ex) {} 
    }

    public void unhighlightLine(int line) {
        try {
            highlightedLines.remove(String.valueOf(line));	
        } catch (Exception ex) {} 
    }

    public boolean isHighlightLine(int line) {
        try { 
            String s = (String) highlightedLines.get(String.valueOf(line));

            if (s == null) {
                return false;
            } else {
                return true;
            }
        } catch (Exception ex) {}
        return false;
    }

    public void mouseDragged(MouseEvent e) {}

    public void mouseClicked(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        int lineToHighlight = 0;
		
        lineToHighlight = (y / componentFontHeight) + 1; // +1 since we start with line one.

        if (isHighlightLine(lineToHighlight)) { 
            unhighlightLine(lineToHighlight);
        } else {
            highlightLine(lineToHighlight);
        }

        repaint();
        // System.out.println(x + " " + y + " clicked! line to highlight is:" +lineToHighlight );
    }
	
    public void mouseEntered(MouseEvent e) {}

    public void mouseExited(MouseEvent e) {}

    public void mousePressed(MouseEvent e) {}

    public void mouseReleased(MouseEvent e) {}

    public void mouseMoved(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        // System.out.println(x + " " + y);
    }

    /**
     *	Convenience constructor for Text Components
     */
    public YLineNumber(JComponent component) {
        if (component == null) {
            setFont(DEFAULT_FONT);
            this.component = this;
        } else {
            setFont(component.getFont());
            this.component = component;
        }

        addMouseListener(this);
        addMouseMotionListener(this);

        setBackground(DEFAULT_BACKGROUND);
        setForeground(DEFAULT_FOREGROUND);
        setPreferredWidth(99);
    }

    /**
     *  Calculate the width needed to display the maximum line number
     */
    public void setPreferredWidth(int lines) {
        int digits = String.valueOf(lines).length();

        // Update sizes when number of digits in the line number changes

        if (digits != currentDigits && digits > 1) {
            currentDigits = digits;
            int width = fontMetrics.charWidth('0') * digits;
            Dimension d = getPreferredSize();

            d.setSize(2 * MARGIN + width, HEIGHT);
            setPreferredSize(d);
            setSize(d);
        }
    }

    /**
     *  Reset variables that are dependent on the font.
     */
    public void setFont(Font font) {
        super.setFont(font);
        fontMetrics = getFontMetrics(getFont());
        componentFontHeight = fontMetrics.getHeight();
        componentFontAscent = fontMetrics.getAscent();
    }

    /**
     *  The line height defaults to the line height of the font for this
     *  component.
     */
    public int getLineHeight() {
        if (lineHeight == 0) {
            return componentFontHeight;
        } else {
            return lineHeight;
        }
    }

    /**
     *  Override the default line height with a positive value.
     *  For example, when you want line numbers for a JTable you could
     *  use the JTable row height.
     */
    public void setLineHeight(int lineHeight) {
        if (lineHeight > 0) {
            this.lineHeight = lineHeight;
        }
    }

    public int getStartOffset() {
        return component.getInsets().top + componentFontAscent;
    }

    public void paintComponent(Graphics g) {
        int lineHeight = getLineHeight();
        int startOffset = getStartOffset();
        Rectangle drawHere = g.getClipBounds();

        // Paint the background

        g.setColor(getBackground());
        g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);

        // Determine the number of lines to draw in the foreground.
        int startLineNumber = (drawHere.y / lineHeight) + 1;
        int endLineNumber = startLineNumber + (drawHere.height / lineHeight);

        int start = (drawHere.y / lineHeight) * lineHeight + startOffset;

        Enumeration e = highlightedLines.keys();
        int y1 = 0;
        int y2 = 0;
        int offs = (componentFontHeight);

        g.setColor(Color.orange);
        int w = getSize().width;

        while (e.hasMoreElements()) { 
            try { 
                String line = (String) e.nextElement();
                int l = Integer.parseInt(line);

                y1 = l * componentFontHeight;
                y2 = ((l * componentFontHeight) + componentFontHeight);
                g.fillRect(0, (y1 - offs) + (componentFontHeight / 4), w + offs,
                        componentFontHeight - 1);
            } catch (Exception ex) { 
                System.out.println(ex);
            }
        }
		
        g.setColor(Color.red);
        e = highlightedLines.keys();
        while (e.hasMoreElements()) { 
            try { 
                String line = (String) e.nextElement();
                int l = Integer.parseInt(line);

                y1 = l * componentFontHeight;
                y2 = ((l * componentFontHeight) + componentFontHeight);
                g.drawRect(0, (y1 - offs) + (componentFontHeight / 4), w + offs,
                        componentFontHeight - 1);
            } catch (Exception ex) { 
                System.out.println(ex);
            }
        }
        g.setColor(getForeground());
		
        for (int i = startLineNumber; i <= endLineNumber; i++) {
            String lineNumber = String.valueOf(i);
            int stringWidth = fontMetrics.stringWidth(lineNumber);
            int rowWidth = getSize().width;
	
            g.drawString(lineNumber, rowWidth - stringWidth - MARGIN, start);
            start += lineHeight;
        }

        int rows = component.getSize().height / componentFontHeight;

        setPreferredWidth(rows);
    }
}
